<template>
    <div
        v-show="value"
        class="vue-image-crop-upload"
    >
        <div class="vicp-wrap">
            <div
                class="vicp-close"
                @click="off"
            >
                <i class="vicp-icon4" />
            </div>

            <div
                v-show="step == 1"
                class="vicp-step1"
            >
                <div
                    class="vicp-drop-area"
                    @dragleave="preventDefault"
                    @dragover="preventDefault"
                    @dragenter="preventDefault"
                    @click="handleClick"
                    @drop="handleChange"
                >
                    <i
                        v-show="loading != 1"
                        class="vicp-icon1"
                    >
                        <i class="vicp-icon1-arrow" />
                        <i class="vicp-icon1-body" />
                        <i class="vicp-icon1-bottom" />
                    </i>
                    <span
                        v-show="loading !== 1"
                        class="vicp-hint"
                    >{{ lang.hint }}</span>
                    <span
                        v-show="!isSupported"
                        class="vicp-no-supported-hint"
                    >{{ lang.noSupported }}</span>
                    <input
                        v-show="false"
                        v-if="step == 1"
                        ref="fileinput"
                        type="file"
                        @change="handleChange"
                    >
                </div>
                <div
                    v-show="hasError"
                    class="vicp-error"
                >
                    <i class="vicp-icon2" />
                    {{ errorMsg }}
                </div>
                <div class="vicp-operate">
                    <a
                        @click="off"
                        @mousedown="ripple"
                    >{{ lang.btn.off }}</a>
                </div>
            </div>

            <div
                v-if="step == 2"
                class="vicp-step2"
            >
                <div class="vicp-crop">
                    <div
                        v-show="true"
                        class="vicp-crop-left"
                    >
                        <div class="vicp-img-container">
                            <img
                                ref="img"
                                :src="sourceImgUrl"
                                :style="sourceImgStyle"
                                class="vicp-img"
                                draggable="false"
                                @drag="preventDefault"
                                @dragstart="preventDefault"
                                @dragend="preventDefault"
                                @dragleave="preventDefault"
                                @dragover="preventDefault"
                                @dragenter="preventDefault"
                                @drop="preventDefault"
                                @touchstart="imgStartMove"
                                @touchmove="imgMove"
                                @touchend="createImg"
                                @touchcancel="createImg"
                                @mousedown="imgStartMove"
                                @mousemove="imgMove"
                                @mouseup="createImg"
                                @mouseout="createImg"
                            >
                            <div
                                :style="sourceImgShadeStyle"
                                class="vicp-img-shade vicp-img-shade-1"
                            />
                            <div
                                :style="sourceImgShadeStyle"
                                class="vicp-img-shade vicp-img-shade-2"
                            />
                        </div>

                        <div class="vicp-range">
                            <input
                                :value="scale.range"
                                type="range"
                                step="1"
                                min="0"
                                max="100"
                                @input="zoomChange"
                            >
                            <i
                                class="vicp-icon5"
                                @mousedown="startZoomSub"
                                @mouseout="endZoomSub"
                                @mouseup="endZoomSub"
                            />
                            <i
                                class="vicp-icon6"
                                @mousedown="startZoomAdd"
                                @mouseout="endZoomAdd"
                                @mouseup="endZoomAdd"
                            />
                        </div>

                        <div
                            v-if="!noRotate"
                            class="vicp-rotate"
                        >
                            <i
                                @mousedown="startRotateLeft"
                                @mouseout="endRotate"
                                @mouseup="endRotate"
                            >↺</i>
                            <i
                                @mousedown="startRotateRight"
                                @mouseout="endRotate"
                                @mouseup="endRotate"
                            >↻</i>
                        </div>
                    </div>
                    <div
                        v-show="true"
                        class="vicp-crop-right"
                    >
                        <div class="vicp-preview">
                            <div
                                v-if="!noSquare"
                                class="vicp-preview-item"
                            >
                                <img
                                    :src="createImgUrl"
                                    :style="previewStyle"
                                >
                                <span>{{ lang.preview }}</span>
                            </div>
                            <div
                                v-if="!noCircle"
                                class="vicp-preview-item vicp-preview-item-circle"
                            >
                                <img
                                    :src="createImgUrl"
                                    :style="previewStyle"
                                >
                                <span>{{ lang.preview }}</span>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="vicp-operate">
                    <a
                        @click="setStep(1)"
                        @mousedown="ripple"
                    >{{ lang.btn.back }}</a>
                    <a
                        class="vicp-operate-btn"
                        @click="prepareUpload"
                        @mousedown="ripple"
                    >{{ lang.btn.save }}</a>
                </div>
            </div>

            <div
                v-if="step == 3"
                class="vicp-step3"
            >
                <div class="vicp-upload">
                    <span
                        v-show="loading === 1"
                        class="vicp-loading"
                    >{{ lang.loading }}</span>
                    <div class="vicp-progress-wrap">
                        <span
                            v-show="loading === 1"
                            :style="progressStyle"
                            class="vicp-progress"
                        />
                    </div>
                    <div
                        v-show="hasError"
                        class="vicp-error"
                    >
                        <i class="vicp-icon2" />
                        {{ errorMsg }}
                    </div>
                    <div
                        v-show="loading === 2"
                        class="vicp-success"
                    >
                        <i class="vicp-icon3" />
                        {{ lang.success }}
                    </div>
                </div>
                <div class="vicp-operate">
                    <a
                        @click="setStep(2)"
                        @mousedown="ripple"
                    >{{ lang.btn.back }}</a>
                    <a
                        @click="off"
                        @mousedown="ripple"
                    >{{ lang.btn.close }}</a>
                </div>
            </div>
            <canvas
                v-show="false"
                ref="canvas"
                :width="width"
                :height="height"
            />
        </div>
    </div>
</template>

<script>
'use strict'
import request from '@/utils/request'
import language from './utils/language.js'
import mimes from './utils/mimes.js'
import data2blob from './utils/data2blob.js'
import effectRipple from './utils/effectRipple.js'
export default {
	props: {
		// 域，上传文件name，触发事件会带上（如果一个页面多个图片上传控件，可以做区分
		field: {
			type: String,
			default: 'avatar'
		},
		// 原名key，类似于id，触发事件会带上（如果一个页面多个图片上传控件，可以做区分
		ki: {
			type: Number,
			default: 0
		},
		// 显示该控件与否
		value: {
			type: Boolean,
			default: true
		},
		// 上传地址
		url: {
			type: String,
			default: ''
		},
		// 其他要上传文件附带的数据，对象格式
		params: {
			type: Object,
			default: null
		},
		// Add custom headers
		headers: {
			type: Object,
			default: null
		},
		// 剪裁图片的宽
		width: {
			type: Number,
			default: 200
		},
		// 剪裁图片的高
		height: {
			type: Number,
			default: 200
		},
		// 不显示旋转功能
		noRotate: {
			type: Boolean,
			default: true
		},
		// 不预览圆形图片
		noCircle: {
			type: Boolean,
			default: false
		},
		// 不预览方形图片
		noSquare: {
			type: Boolean,
			default: false
		},
		// 单文件大小限制
		maxSize: {
			type: Number,
			default: 10240
		},
		// 语言类型
		langType: {
			type: String,
			default: 'zh'
		},
		// 语言包
		langExt: {
			type: Object,
			default: null
		},
		// 图片上传格式
		imgFormat: {
			type: String,
			default: 'png'
		},
		// 是否支持跨域
		withCredentials: {
			type: Boolean,
			default: false
		}
	},
	data() {
		const { imgFormat, langType, langExt, width, height } = this
		let isSupported = true
		const allowImgFormat = ['jpg', 'png']
		const tempImgFormat = allowImgFormat.indexOf(imgFormat) === -1 ? 'jpg' : imgFormat
		const lang = language[langType] ? language[langType] : language['en']
		const mime = mimes[tempImgFormat]
		// 规范图片格式
		this.imgFormat = tempImgFormat
		if (langExt) {
			Object.assign(lang, langExt)
		}
		if (typeof FormData !== 'function') {
			isSupported = false
		}
		return {
			// 图片的mime
			mime,
			// 语言包
			lang,
			// 浏览器是否支持该控件
			isSupported,
			// 浏览器是否支持触屏事件
			// eslint-disable-next-line no-prototype-builtins
			isSupportTouch: document.hasOwnProperty('ontouchstart'),
			// 步骤
			step: 1, // 1选择文件 2剪裁 3上传
			// 上传状态及进度
			loading: 0, // 0未开始 1正在 2成功 3错误
			progress: 0,
			// 是否有错误及错误信息
			hasError: false,
			errorMsg: '',
			// 需求图宽高比
			ratio: width / height,
			// 原图地址、生成图片地址
			sourceImg: null,
			sourceImgUrl: '',
			createImgUrl: '',
			// 原图片拖动事件初始值
			sourceImgMouseDown: {
				on: false,
				mX: 0, // 鼠标按下的坐标
				mY: 0,
				x: 0, // scale原图坐标
				y: 0
			},
			// 生成图片预览的容器大小
			previewContainer: {
				width: 100,
				height: 100
			},
			// 原图容器宽高
			sourceImgContainer: {
				// sic
				width: 240,
				height: 184 // 如果生成图比例与此一致会出现bug，先改成特殊的格式吧，哈哈哈
			},
			// 原图展示属性
			scale: {
				zoomAddOn: false, // 按钮缩放事件开启
				zoomSubOn: false, // 按钮缩放事件开启
				range: 1, // 最大100
				rotateLeft: false, // 按钮向左旋转事件开启
				rotateRight: false, // 按钮向右旋转事件开启
				degree: 0, // 旋转度数
				x: 0,
				y: 0,
				width: 0,
				height: 0,
				maxWidth: 0,
				maxHeight: 0,
				minWidth: 0, // 最宽
				minHeight: 0,
				naturalWidth: 0, // 原宽
				naturalHeight: 0
			}
		}
	},
	computed: {
		// 进度条样式
		progressStyle() {
			const { progress } = this
			return {
				width: progress + '%'
			}
		},
		// 原图样式
		sourceImgStyle() {
			const { scale, sourceImgMasking } = this
			const top = scale.y + sourceImgMasking.y + 'px'
			const left = scale.x + sourceImgMasking.x + 'px'
			return {
				top,
				left,
				width: scale.width + 'px',
				height: scale.height + 'px',
				transform: 'rotate(' + scale.degree + 'deg)', // 旋转时 左侧原始图旋转样式
				'-ms-transform': 'rotate(' + scale.degree + 'deg)', // 兼容IE9
				'-moz-transform': 'rotate(' + scale.degree + 'deg)', // 兼容FireFox
				'-webkit-transform': 'rotate(' + scale.degree + 'deg)', // 兼容Safari 和 chrome
				'-o-transform': 'rotate(' + scale.degree + 'deg)' // 兼容 Opera
			}
		},
		// 原图蒙版属性
		sourceImgMasking() {
			const { width, height, ratio, sourceImgContainer } = this
			const sic = sourceImgContainer
			const sicRatio = sic.width / sic.height // 原图容器宽高比
			let x = 0
			let y = 0
			let w = sic.width
			let h = sic.height
			let scale = 1
			if (ratio < sicRatio) {
				scale = sic.height / height
				w = sic.height * ratio
				x = (sic.width - w) / 2
			}
			if (ratio > sicRatio) {
				scale = sic.width / width
				h = sic.width / ratio
				y = (sic.height - h) / 2
			}
			return {
				scale, // 蒙版相对需求宽高的缩放
				x,
				y,
				width: w,
				height: h
			}
		},
		// 原图遮罩样式
		sourceImgShadeStyle() {
			const { sourceImgMasking, sourceImgContainer } = this
			const sic = sourceImgContainer
			const sim = sourceImgMasking
			const w = sim.width === sic.width ? sim.width : (sic.width - sim.width) / 2
			const h = sim.height === sic.height ? sim.height : (sic.height - sim.height) / 2
			return {
				width: w + 'px',
				height: h + 'px'
			}
		},
		previewStyle() {
			const { ratio, previewContainer } = this
			const pc = previewContainer
			let w = pc.width
			let h = pc.height
			const pcRatio = w / h
			if (ratio < pcRatio) {
				w = pc.height * ratio
			}
			if (ratio > pcRatio) {
				h = pc.width / ratio
			}
			return {
				width: w + 'px',
				height: h + 'px'
			}
		}
	},
	watch: {
		value(newValue) {
			if (newValue && this.loading !== 1) {
				this.reset()
			}
		}
	},
	created() {
		// 绑定按键esc隐藏此插件事件
		document.addEventListener('keyup', this.closeHandler)
	},
	destroyed() {
		document.removeEventListener('keyup', this.closeHandler)
	},
	methods: {
		// 点击波纹效果
		ripple(e) {
			effectRipple(e)
		},
		// 关闭控件
		off() {
			setTimeout(() => {
				this.$emit('input', false)
				this.$emit('close')
				if (this.step === 3 && this.loading === 2) {
					this.setStep(1)
				}
			}, 200)
		},
		// 设置步骤
		setStep(no) {
			// 延时是为了显示动画效果呢，哈哈哈
			setTimeout(() => {
				this.step = no
			}, 200)
		},
		/* 图片选择区域函数绑定
     ---------------------------------------------------------------*/
		preventDefault(e) {
			e.preventDefault()
			return false
		},
		handleClick(e) {
			if (this.loading !== 1) {
				if (e.target !== this.$refs.fileinput) {
					e.preventDefault()
					if (document.activeElement !== this.$refs) {
						this.$refs.fileinput.click()
					}
				}
			}
		},
		handleChange(e) {
			e.preventDefault()
			if (this.loading !== 1) {
				const files = e.target.files || e.dataTransfer.files
				this.reset()
				if (this.checkFile(files[0])) {
					this.setSourceImg(files[0])
				}
			}
		},
		/* ---------------------------------------------------------------*/
		// 检测选择的文件是否合适
		checkFile(file) {
			const { lang, maxSize } = this
			// 仅限图片
			if (file.type.indexOf('image') === -1) {
				this.hasError = true
				this.errorMsg = lang.error.onlyImg
				return false
			}
			// 超出大小
			if (file.size / 1024 > maxSize) {
				this.hasError = true
				this.errorMsg = lang.error.outOfSize + maxSize + 'kb'
				return false
			}
			return true
		},
		// 重置控件
		reset() {
			this.loading = 0
			this.hasError = false
			this.errorMsg = ''
			this.progress = 0
		},
		// 设置图片源
		setSourceImg(file) {
			const fr = new FileReader()
			fr.onload = (e) => {
				this.sourceImgUrl = fr.result
				this.startCrop()
			}
			fr.readAsDataURL(file)
		},
		// 剪裁前准备工作
		startCrop() {
			const { width, height, ratio, scale, sourceImgUrl, sourceImgMasking, lang } = this
			const sim = sourceImgMasking
			const img = new Image()
			img.src = sourceImgUrl
			img.onload = () => {
				const nWidth = img.naturalWidth
				const nHeight = img.naturalHeight
				const nRatio = nWidth / nHeight
				let w = sim.width
				let h = sim.height
				let x = 0
				let y = 0
				// 图片像素不达标
				if (nWidth < width || nHeight < height) {
					this.hasError = true
					this.errorMsg = lang.error.lowestPx + width + '*' + height
					return false
				}
				if (ratio > nRatio) {
					h = w / nRatio
					y = (sim.height - h) / 2
				}
				if (ratio < nRatio) {
					w = h * nRatio
					x = (sim.width - w) / 2
				}
				scale.range = 0
				scale.x = x
				scale.y = y
				scale.width = w
				scale.height = h
				scale.degree = 0
				scale.minWidth = w
				scale.minHeight = h
				scale.maxWidth = nWidth * sim.scale
				scale.maxHeight = nHeight * sim.scale
				scale.naturalWidth = nWidth
				scale.naturalHeight = nHeight
				this.sourceImg = img
				this.createImg()
				this.setStep(2)
			}
		},
		// 鼠标按下图片准备移动
		imgStartMove(e) {
			e.preventDefault()
			// 支持触摸事件，则鼠标事件无效
			if (this.isSupportTouch && !e.targetTouches) {
				return false
			}
			const et = e.targetTouches ? e.targetTouches[0] : e
			const { sourceImgMouseDown, scale } = this
			const simd = sourceImgMouseDown
			simd.mX = et.screenX
			simd.mY = et.screenY
			simd.x = scale.x
			simd.y = scale.y
			simd.on = true
		},
		// 鼠标按下状态下移动，图片移动
		imgMove(e) {
			e.preventDefault()
			// 支持触摸事件，则鼠标事件无效
			if (this.isSupportTouch && !e.targetTouches) {
				return false
			}
			const et = e.targetTouches ? e.targetTouches[0] : e
			const {
				sourceImgMouseDown: { on, mX, mY, x, y },
				scale,
				sourceImgMasking
			} = this
			const sim = sourceImgMasking
			const nX = et.screenX
			const nY = et.screenY
			const dX = nX - mX
			const dY = nY - mY
			let rX = x + dX
			let rY = y + dY
			if (!on) return
			if (rX > 0) {
				rX = 0
			}
			if (rY > 0) {
				rY = 0
			}
			if (rX < sim.width - scale.width) {
				rX = sim.width - scale.width
			}
			if (rY < sim.height - scale.height) {
				rY = sim.height - scale.height
			}
			scale.x = rX
			scale.y = rY
		},
		// 按钮按下开始向右旋转
		startRotateRight(e) {
			const { scale } = this
			scale.rotateRight = true
			const rotate = () => {
				if (scale.rotateRight) {
					const degree = ++scale.degree
					this.createImg(degree)
					setTimeout(function () {
						rotate()
					}, 60)
				}
			}
			rotate()
		},
		// 按钮按下开始向左旋转
		startRotateLeft(e) {
			const { scale } = this
			scale.rotateLeft = true
			const rotate = () => {
				if (scale.rotateLeft) {
					const degree = --scale.degree
					this.createImg(degree)
					setTimeout(function () {
						rotate()
					}, 60)
				}
			}
			rotate()
		},
		// 停止旋转
		endRotate() {
			const { scale } = this
			scale.rotateLeft = false
			scale.rotateRight = false
		},
		// 按钮按下开始放大
		startZoomAdd(e) {
			const { scale } = this
			scale.zoomAddOn = true
			const zoom = () => {
				if (scale.zoomAddOn) {
					const range = scale.range >= 100 ? 100 : ++scale.range
					this.zoomImg(range)
					setTimeout(function () {
						zoom()
					}, 60)
				}
			}
			zoom()
		},
		// 按钮松开或移开取消放大
		endZoomAdd(e) {
			this.scale.zoomAddOn = false
		},
		// 按钮按下开始缩小
		startZoomSub(e) {
			const { scale } = this
			scale.zoomSubOn = true
			const zoom = () => {
				if (scale.zoomSubOn) {
					const range = scale.range <= 0 ? 0 : --scale.range
					this.zoomImg(range)
					setTimeout(function () {
						zoom()
					}, 60)
				}
			}
			zoom()
		},
		// 按钮松开或移开取消缩小
		endZoomSub(e) {
			const { scale } = this
			scale.zoomSubOn = false
		},
		zoomChange(e) {
			this.zoomImg(e.target.value)
		},
		// 缩放原图
		zoomImg(newRange) {
			const { sourceImgMasking, scale } = this
			const { maxWidth, maxHeight, minWidth, minHeight, width, height, x, y } = scale
			const sim = sourceImgMasking
			// 蒙版宽高
			const sWidth = sim.width
			const sHeight = sim.height
			// 新宽高
			const nWidth = minWidth + ((maxWidth - minWidth) * newRange) / 100
			const nHeight = minHeight + ((maxHeight - minHeight) * newRange) / 100
			// 新坐标（根据蒙版中心点缩放）
			let nX = sWidth / 2 - (nWidth / width) * (sWidth / 2 - x)
			let nY = sHeight / 2 - (nHeight / height) * (sHeight / 2 - y)
			// 判断新坐标是否超过蒙版限制
			if (nX > 0) {
				nX = 0
			}
			if (nY > 0) {
				nY = 0
			}
			if (nX < sWidth - nWidth) {
				nX = sWidth - nWidth
			}
			if (nY < sHeight - nHeight) {
				nY = sHeight - nHeight
			}
			// 赋值处理
			scale.x = nX
			scale.y = nY
			scale.width = nWidth
			scale.height = nHeight
			scale.range = newRange
			setTimeout(() => {
				if (scale.range === newRange) {
					this.createImg()
				}
			}, 300)
		},
		// 生成需求图片
		createImg(e) {
			const {
				mime,
				sourceImg,
				scale: { x, y, width, height, degree },
				sourceImgMasking: { scale }
			} = this
			const canvas = this.$refs.canvas
			const ctx = canvas.getContext('2d')
			if (e) {
				// 取消鼠标按下移动状态
				this.sourceImgMouseDown.on = false
			}
			canvas.width = this.width
			canvas.height = this.height
			ctx.clearRect(0, 0, this.width, this.height)
			// 将透明区域设置为白色底边
			ctx.fillStyle = '#fff'
			ctx.fillRect(0, 0, this.width, this.height)
			ctx.translate(this.width * 0.5, this.height * 0.5)
			ctx.rotate((Math.PI * degree) / 180)
			ctx.translate(-this.width * 0.5, -this.height * 0.5)
			ctx.drawImage(sourceImg, x / scale, y / scale, width / scale, height / scale)
			this.createImgUrl = canvas.toDataURL(mime)
		},
		prepareUpload() {
			const { url, createImgUrl, field, ki } = this
			this.$emit('crop-success', createImgUrl, field, ki)
			if (typeof url === 'string' && url) {
				this.upload()
			} else {
				this.off()
			}
		},
		// 上传图片
		upload() {
			const { lang, imgFormat, mime, url, params, field, ki, createImgUrl } = this
			const fmData = new FormData()
			fmData.append(field, data2blob(createImgUrl, mime), field + '.' + imgFormat)
			// 添加其他参数
			if (typeof params === 'object' && params) {
				Object.keys(params).forEach((k) => {
					fmData.append(k, params[k])
				})
			}
			// 监听进度回调
			// const uploadProgress = (event) => {
			//   if (event.lengthComputable) {
			//     this.progress = 100 * Math.round(event.loaded) / event.total
			//   }
			// }
			// 上传文件
			this.reset()
			this.loading = 1
			this.setStep(3)
			request({
				url,
				method: 'post',
				data: fmData
			})
				.then((resData) => {
					this.loading = 2
					this.$emit('crop-upload-success', resData.data)
				})
				.catch((err) => {
					if (this.value) {
						this.loading = 3
						this.hasError = true
						this.errorMsg = lang.fail
						this.$emit('crop-upload-fail', err, field, ki)
					}
				})
		},
		closeHandler(e) {
			if (this.value && (e.key === 'Escape' || e.keyCode === 27)) {
				this.off()
			}
		}
	}
}
</script>

<style lang="scss">
@charset "UTF-8";
@-webkit-keyframes vicp_progress {
	0% {
		background-position-y: 0;
	}
	100% {
		background-position-y: 40px;
	}
}
@keyframes vicp_progress {
	0% {
		background-position-y: 0;
	}
	100% {
		background-position-y: 40px;
	}
}
@-webkit-keyframes vicp {
	0% {
		opacity: 0;
		-webkit-transform: scale(0) translatey(-60px);
		transform: scale(0) translatey(-60px);
	}
	100% {
		opacity: 1;
		-webkit-transform: scale(1) translatey(0);
		transform: scale(1) translatey(0);
	}
}
@keyframes vicp {
	0% {
		opacity: 0;
		-webkit-transform: scale(0) translatey(-60px);
		transform: scale(0) translatey(-60px);
	}
	100% {
		opacity: 1;
		-webkit-transform: scale(1) translatey(0);
		transform: scale(1) translatey(0);
	}
}
.vue-image-crop-upload {
	position: fixed;
	display: block;
	-webkit-box-sizing: border-box;
	box-sizing: border-box;
	z-index: 10000;
	top: 0;
	bottom: 0;
	left: 0;
	right: 0;
	width: 100%;
	height: 100%;
	background-color: rgba(0, 0, 0, 0.65);
	-webkit-tap-highlight-color: transparent;
	-moz-tap-highlight-color: transparent;
}
.vue-image-crop-upload .vicp-wrap {
	-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
	box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
	position: fixed;
	display: block;
	-webkit-box-sizing: border-box;
	box-sizing: border-box;
	z-index: 10000;
	top: 0;
	bottom: 0;
	left: 0;
	right: 0;
	margin: auto;
	width: 600px;
	height: 330px;
	padding: 25px;
	background-color: #fff;
	border-radius: 2px;
	-webkit-animation: vicp 0.12s ease-in;
	animation: vicp 0.12s ease-in;
}
.vue-image-crop-upload .vicp-wrap .vicp-close {
	position: absolute;
	right: -30px;
	top: -30px;
}
.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4 {
	position: relative;
	display: block;
	width: 30px;
	height: 30px;
	cursor: pointer;
	-webkit-transition: -webkit-transform 0.18s;
	transition: -webkit-transform 0.18s;
	transition: transform 0.18s;
	transition: transform 0.18s, -webkit-transform 0.18s;
	-webkit-transform: rotate(0);
	-ms-transform: rotate(0);
	transform: rotate(0);
}
.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::after,
.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::before {
	-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
	box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
	content: '';
	position: absolute;
	top: 12px;
	left: 4px;
	width: 20px;
	height: 3px;
	-webkit-transform: rotate(45deg);
	-ms-transform: rotate(45deg);
	transform: rotate(45deg);
	background-color: #fff;
}
.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::after {
	-webkit-transform: rotate(-45deg);
	-ms-transform: rotate(-45deg);
	transform: rotate(-45deg);
}
.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4:hover {
	-webkit-transform: rotate(90deg);
	-ms-transform: rotate(90deg);
	transform: rotate(90deg);
}
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area {
	position: relative;
	-webkit-box-sizing: border-box;
	box-sizing: border-box;
	padding: 35px;
	height: 170px;
	background-color: rgba(0, 0, 0, 0.03);
	text-align: center;
	border: 1px dashed rgba(0, 0, 0, 0.08);
	overflow: hidden;
}
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 {
	display: block;
	margin: 0 auto 6px;
	width: 42px;
	height: 42px;
	overflow: hidden;
}
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 .vicp-icon1-arrow {
	display: block;
	margin: 0 auto;
	width: 0;
	height: 0;
	border-bottom: 14.7px solid rgba(0, 0, 0, 0.3);
	border-left: 14.7px solid transparent;
	border-right: 14.7px solid transparent;
}
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 .vicp-icon1-body {
	display: block;
	width: 12.6px;
	height: 14.7px;
	margin: 0 auto;
	background-color: rgba(0, 0, 0, 0.3);
}
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 .vicp-icon1-bottom {
	-webkit-box-sizing: border-box;
	box-sizing: border-box;
	display: block;
	height: 12.6px;
	border: 6px solid rgba(0, 0, 0, 0.3);
	border-top: none;
}
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-hint {
	display: block;
	padding: 15px;
	font-size: 14px;
	color: #666;
	line-height: 30px;
}
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-no-supported-hint {
	display: block;
	position: absolute;
	top: 0;
	left: 0;
	padding: 30px;
	width: 100%;
	height: 60px;
	line-height: 30px;
	background-color: #eee;
	text-align: center;
	color: #666;
	font-size: 14px;
}
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area:hover {
	cursor: pointer;
	border-color: rgba(0, 0, 0, 0.1);
	background-color: rgba(0, 0, 0, 0.05);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop {
	overflow: hidden;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left {
	float: left;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container {
	position: relative;
	display: block;
	width: 240px;
	height: 180px;
	background-color: #e5e5e0;
	overflow: hidden;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img {
	position: absolute;
	display: block;
	cursor: move;
	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img-shade {
	-webkit-box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
	box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
	position: absolute;
	background-color: rgba(241, 242, 243, 0.8);
}
.vue-image-crop-upload
	.vicp-wrap
	.vicp-step2
	.vicp-crop
	.vicp-crop-left
	.vicp-img-container
	.vicp-img-shade.vicp-img-shade-1 {
	top: 0;
	left: 0;
}
.vue-image-crop-upload
	.vicp-wrap
	.vicp-step2
	.vicp-crop
	.vicp-crop-left
	.vicp-img-container
	.vicp-img-shade.vicp-img-shade-2 {
	bottom: 0;
	right: 0;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-rotate {
	position: relative;
	width: 240px;
	height: 18px;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-rotate i {
	display: block;
	width: 18px;
	height: 18px;
	border-radius: 100%;
	line-height: 18px;
	text-align: center;
	font-size: 12px;
	font-weight: bold;
	background-color: rgba(0, 0, 0, 0.08);
	color: #fff;
	overflow: hidden;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-rotate i:hover {
	-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
	box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
	cursor: pointer;
	background-color: rgba(0, 0, 0, 0.14);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-rotate i:first-child {
	float: left;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-rotate i:last-child {
	float: right;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range {
	position: relative;
	margin: 30px 0 10px 0;
	width: 240px;
	height: 18px;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5,
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6 {
	position: absolute;
	top: 0;
	width: 18px;
	height: 18px;
	border-radius: 100%;
	background-color: rgba(0, 0, 0, 0.08);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5:hover,
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6:hover {
	-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
	box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
	cursor: pointer;
	background-color: rgba(0, 0, 0, 0.14);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5 {
	left: 0;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5::before {
	position: absolute;
	content: '';
	display: block;
	left: 3px;
	top: 8px;
	width: 12px;
	height: 2px;
	background-color: #fff;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6 {
	right: 0;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6::before {
	position: absolute;
	content: '';
	display: block;
	left: 3px;
	top: 8px;
	width: 12px;
	height: 2px;
	background-color: #fff;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6::after {
	position: absolute;
	content: '';
	display: block;
	top: 3px;
	left: 8px;
	width: 2px;
	height: 12px;
	background-color: #fff;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type='range'] {
	display: block;
	padding-top: 5px;
	margin: 0 auto;
	width: 180px;
	height: 8px;
	vertical-align: top;
	background: transparent;
	-webkit-appearance: none;
	-moz-appearance: none;
	appearance: none;
	cursor: pointer;
	/* 滑块
               ---------------------------------------------------------------*/
	/* 轨道
               ---------------------------------------------------------------*/
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type='range']:focus {
	outline: none;
}
.vue-image-crop-upload
	.vicp-wrap
	.vicp-step2
	.vicp-crop
	.vicp-crop-left
	.vicp-range
	input[type='range']::-webkit-slider-thumb {
	-webkit-box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
	box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
	-webkit-appearance: none;
	appearance: none;
	margin-top: -3px;
	width: 12px;
	height: 12px;
	background-color: #61c091;
	border-radius: 100%;
	border: none;
	-webkit-transition: 0.2s;
	transition: 0.2s;
}
.vue-image-crop-upload
	.vicp-wrap
	.vicp-step2
	.vicp-crop
	.vicp-crop-left
	.vicp-range
	input[type='range']::-moz-range-thumb {
	box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
	-moz-appearance: none;
	appearance: none;
	width: 12px;
	height: 12px;
	background-color: #61c091;
	border-radius: 100%;
	border: none;
	-webkit-transition: 0.2s;
	transition: 0.2s;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type='range']::-ms-thumb {
	box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
	appearance: none;
	width: 12px;
	height: 12px;
	background-color: #61c091;
	border: none;
	border-radius: 100%;
	-webkit-transition: 0.2s;
	transition: 0.2s;
}
.vue-image-crop-upload
	.vicp-wrap
	.vicp-step2
	.vicp-crop
	.vicp-crop-left
	.vicp-range
	input[type='range']:active::-moz-range-thumb {
	box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
	width: 14px;
	height: 14px;
}
.vue-image-crop-upload
	.vicp-wrap
	.vicp-step2
	.vicp-crop
	.vicp-crop-left
	.vicp-range
	input[type='range']:active::-ms-thumb {
	box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
	width: 14px;
	height: 14px;
}
.vue-image-crop-upload
	.vicp-wrap
	.vicp-step2
	.vicp-crop
	.vicp-crop-left
	.vicp-range
	input[type='range']:active::-webkit-slider-thumb {
	-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
	box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
	margin-top: -4px;
	width: 14px;
	height: 14px;
}
.vue-image-crop-upload
	.vicp-wrap
	.vicp-step2
	.vicp-crop
	.vicp-crop-left
	.vicp-range
	input[type='range']::-webkit-slider-runnable-track {
	-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
	box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
	width: 100%;
	height: 6px;
	cursor: pointer;
	border-radius: 2px;
	border: none;
	background-color: rgba(68, 170, 119, 0.3);
}
.vue-image-crop-upload
	.vicp-wrap
	.vicp-step2
	.vicp-crop
	.vicp-crop-left
	.vicp-range
	input[type='range']::-moz-range-track {
	box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
	width: 100%;
	height: 6px;
	cursor: pointer;
	border-radius: 2px;
	border: none;
	background-color: rgba(68, 170, 119, 0.3);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type='range']::-ms-track {
	box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
	width: 100%;
	cursor: pointer;
	background: transparent;
	border-color: transparent;
	color: transparent;
	height: 6px;
	border-radius: 2px;
	border: none;
}
.vue-image-crop-upload
	.vicp-wrap
	.vicp-step2
	.vicp-crop
	.vicp-crop-left
	.vicp-range
	input[type='range']::-ms-fill-lower {
	background-color: rgba(68, 170, 119, 0.3);
}
.vue-image-crop-upload
	.vicp-wrap
	.vicp-step2
	.vicp-crop
	.vicp-crop-left
	.vicp-range
	input[type='range']::-ms-fill-upper {
	background-color: rgba(68, 170, 119, 0.15);
}
.vue-image-crop-upload
	.vicp-wrap
	.vicp-step2
	.vicp-crop
	.vicp-crop-left
	.vicp-range
	input[type='range']:focus::-webkit-slider-runnable-track {
	background-color: rgba(68, 170, 119, 0.5);
}
.vue-image-crop-upload
	.vicp-wrap
	.vicp-step2
	.vicp-crop
	.vicp-crop-left
	.vicp-range
	input[type='range']:focus::-moz-range-track {
	background-color: rgba(68, 170, 119, 0.5);
}
.vue-image-crop-upload
	.vicp-wrap
	.vicp-step2
	.vicp-crop
	.vicp-crop-left
	.vicp-range
	input[type='range']:focus::-ms-fill-lower {
	background-color: rgba(68, 170, 119, 0.45);
}
.vue-image-crop-upload
	.vicp-wrap
	.vicp-step2
	.vicp-crop
	.vicp-crop-left
	.vicp-range
	input[type='range']:focus::-ms-fill-upper {
	background-color: rgba(68, 170, 119, 0.25);
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right {
	float: right;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview {
	height: 150px;
	overflow: hidden;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item {
	position: relative;
	padding: 5px;
	width: 100px;
	height: 100px;
	float: left;
	margin-right: 16px;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item span {
	position: absolute;
	bottom: -30px;
	width: 100%;
	font-size: 14px;
	color: #bbb;
	display: block;
	text-align: center;
}
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item img {
	position: absolute;
	display: block;
	top: 0;
	bottom: 0;
	left: 0;
	right: 0;
	margin: auto;
	padding: 3px;
	background-color: #fff;
	border: 1px solid rgba(0, 0, 0, 0.15);
	overflow: hidden;
	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
}
.vue-image-crop-upload
	.vicp-wrap
	.vicp-step2
	.vicp-crop
	.vicp-crop-right
	.vicp-preview
	.vicp-preview-item.vicp-preview-item-circle {
	margin-right: 0;
}
.vue-image-crop-upload
	.vicp-wrap
	.vicp-step2
	.vicp-crop
	.vicp-crop-right
	.vicp-preview
	.vicp-preview-item.vicp-preview-item-circle
	img {
	border-radius: 100%;
}
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload {
	position: relative;
	-webkit-box-sizing: border-box;
	box-sizing: border-box;
	padding: 35px;
	height: 170px;
	background-color: rgba(0, 0, 0, 0.03);
	text-align: center;
	border: 1px dashed #ddd;
}
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-loading {
	display: block;
	padding: 15px;
	font-size: 16px;
	color: #999;
	line-height: 30px;
}
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-progress-wrap {
	margin-top: 12px;
	background-color: rgba(0, 0, 0, 0.08);
	border-radius: 3px;
}
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-progress-wrap .vicp-progress {
	position: relative;
	display: block;
	height: 5px;
	border-radius: 3px;
	background-color: #4a7;
	-webkit-box-shadow: 0 2px 6px 0 rgba(68, 170, 119, 0.3);
	box-shadow: 0 2px 6px 0 rgba(68, 170, 119, 0.3);
	-webkit-transition: width 0.15s linear;
	transition: width 0.15s linear;
	background-image: -webkit-linear-gradient(
		135deg,
		rgba(255, 255, 255, 0.2) 25%,
		transparent 25%,
		transparent 50%,
		rgba(255, 255, 255, 0.2) 50%,
		rgba(255, 255, 255, 0.2) 75%,
		transparent 75%,
		transparent
	);
	background-image: linear-gradient(
		-45deg,
		rgba(255, 255, 255, 0.2) 25%,
		transparent 25%,
		transparent 50%,
		rgba(255, 255, 255, 0.2) 50%,
		rgba(255, 255, 255, 0.2) 75%,
		transparent 75%,
		transparent
	);
	background-size: 40px 40px;
	-webkit-animation: vicp_progress 0.5s linear infinite;
	animation: vicp_progress 0.5s linear infinite;
}
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-progress-wrap .vicp-progress::after {
	content: '';
	position: absolute;
	display: block;
	top: -3px;
	right: -3px;
	width: 9px;
	height: 9px;
	border: 1px solid rgba(245, 246, 247, 0.7);
	-webkit-box-shadow: 0 1px 4px 0 rgba(68, 170, 119, 0.7);
	box-shadow: 0 1px 4px 0 rgba(68, 170, 119, 0.7);
	border-radius: 100%;
	background-color: #4a7;
}
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-error,
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-success {
	height: 100px;
	line-height: 100px;
}
.vue-image-crop-upload .vicp-wrap .vicp-operate {
	position: absolute;
	right: 20px;
	bottom: 20px;
}
.vue-image-crop-upload .vicp-wrap .vicp-operate a {
	position: relative;
	float: left;
	display: block;
	margin-left: 10px;
	width: 100px;
	height: 36px;
	line-height: 36px;
	text-align: center;
	cursor: pointer;
	font-size: 14px;
	color: #4a7;
	border-radius: 2px;
	overflow: hidden;
	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
}
.vue-image-crop-upload .vicp-wrap .vicp-operate a:hover {
	background-color: rgba(0, 0, 0, 0.03);
}
.vue-image-crop-upload .vicp-wrap .vicp-error,
.vue-image-crop-upload .vicp-wrap .vicp-success {
	display: block;
	font-size: 14px;
	line-height: 24px;
	height: 24px;
	color: #d10;
	text-align: center;
	vertical-align: top;
}
.vue-image-crop-upload .vicp-wrap .vicp-success {
	color: #4a7;
}
.vue-image-crop-upload .vicp-wrap .vicp-icon3 {
	position: relative;
	display: inline-block;
	width: 20px;
	height: 20px;
	top: 4px;
}
.vue-image-crop-upload .vicp-wrap .vicp-icon3::after {
	position: absolute;
	top: 3px;
	left: 6px;
	width: 6px;
	height: 10px;
	border-width: 0 2px 2px 0;
	border-color: #4a7;
	border-style: solid;
	-webkit-transform: rotate(45deg);
	-ms-transform: rotate(45deg);
	transform: rotate(45deg);
	content: '';
}
.vue-image-crop-upload .vicp-wrap .vicp-icon2 {
	position: relative;
	display: inline-block;
	width: 20px;
	height: 20px;
	top: 4px;
}
.vue-image-crop-upload .vicp-wrap .vicp-icon2::after,
.vue-image-crop-upload .vicp-wrap .vicp-icon2::before {
	content: '';
	position: absolute;
	top: 9px;
	left: 4px;
	width: 13px;
	height: 2px;
	background-color: #d10;
	-webkit-transform: rotate(45deg);
	-ms-transform: rotate(45deg);
	transform: rotate(45deg);
}
.vue-image-crop-upload .vicp-wrap .vicp-icon2::after {
	-webkit-transform: rotate(-45deg);
	-ms-transform: rotate(-45deg);
	transform: rotate(-45deg);
}
.e-ripple {
	position: absolute;
	border-radius: 100%;
	background-color: rgba(0, 0, 0, 0.15);
	background-clip: padding-box;
	pointer-events: none;
	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
	-webkit-transform: scale(0);
	-ms-transform: scale(0);
	transform: scale(0);
	opacity: 1;
}
.e-ripple.z-active {
	opacity: 0;
	-webkit-transform: scale(2);
	-ms-transform: scale(2);
	transform: scale(2);
	-webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
	transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
	transition: opacity 1.2s ease-out, transform 0.6s ease-out;
	transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out;
}
</style>
